﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AMS.Core.ExtensionMethods;

namespace AMS.Core.Validation
{
    public class PasswordValidator
    {
        // one and only one valid definition of a password!
        // TODO: Find a better one to replace this with

        static object lockObject = new Object();
        static PasswordValidator _singleton;

        #region Properties
        public int MinLength { get; set; }
        public int MaxLength { get; set; }
        public int RequiredUpper { get; set; }
        public int RequiredLower { get; set; }
        public int RequiredNumber { get; set; }
        public int RequiredSpecial { get; set; }

        HashSet<char> _forbiddenChars;
        public IEnumerable<char> ForbiddenChars
        {
            get { return _forbiddenChars; }
        } 
        #endregion

        #region Constructors
        private PasswordValidator()
        {
            // simple default for now
            MinLength = 4;
            _forbiddenChars = new HashSet<char>();
        }

        public static PasswordValidator GetSingleInstance()
        {
            if (_singleton == null)
            {
                lock (lockObject)
                {
                    if (_singleton == null)
                    {
                        _singleton = new PasswordValidator();
                    }
                }
            }

            return _singleton;
        } 
        #endregion

        public void AddForbiddenCharacter(char c)
        {
            if (!_forbiddenChars.Contains(c))
                _forbiddenChars.Add(c);
        }

        public bool ValidatePassword(string password)
        {
            if (String.IsNullOrEmpty(password))
                return false;

            if (password.ContainsWhiteSpace())
                return false;

            if (!ValidateLength(password))
                return false;

            if (!ValidateForbidden(password))
                return false;

            if (!ValidateContents(password))
                return false;

            return true;
        }

        private bool ValidateLength(string password)
        {
            if (password.Length < MinLength)
                return false;

            if (MaxLength > 0 && password.Length > MaxLength)
                return false;

            return true;
        }

        private bool ValidateForbidden(string password)
        {
            foreach (char c in _forbiddenChars)
            {
                if (password.Contains(c))
                    return false;
            }

            return true;
        }

        private bool ValidateContents(string password)
        {
            if (!ValidateUpper(password))
                return false;
            if (!ValidateLower(password))
                return false;
            if (!ValidateNumber(password))
                return false;
            if (!ValidateSpecial(password))
                return false;

            return true;
        }

        private bool ValidateUpper(string password)
        {
            int i = 0;

            foreach (char c in password)
            {
                if (Char.IsUpper(c))
                    i++;

                if (i >= RequiredUpper)
                    return true;
            }

            return false;
        }

        private bool ValidateLower(string password)
        {
            int i = 0;

            foreach (char c in password)
            {
                if (Char.IsLower(c))
                    i++;

                if (i >= RequiredLower)
                    return true;
            }

            return false;
        }

        private bool ValidateNumber(string password)
        {
            int i = 0;

            foreach (char c in password)
            {
                if (Char.IsNumber(c))
                    i++;

                if (i >= RequiredNumber)
                    return true;
            }

            return false;
        }

        private bool ValidateSpecial(string password)
        {
            int i = 0;

            foreach (char c in password)
            {
                if (Char.IsPunctuation(c) || Char.IsSymbol(c))
                    i++;

                if (i >= RequiredUpper)
                    return true;
            }

            return false;
        }
    }
}
